#include "shared.h"

t_bitmap bitmap;
t_input input;
t_snd snd;
int memory_allocated = 0;
static int sound_tbl[262];
static int use_cpu68k_type;

#include "menu.h"

#define SND_SIZE (snd.buffer_size * sizeof(int16))

int xrender_init(int xvdp);

#define vclk 7670454
#define zclk 3579545

int audio_init(int rate)
{
  int i;

  /* Clear the sound data context */
  // Do not wipe out the buffer pointers
  snd.sample_rate = 0;
  snd.enabled = 0;
  snd.buffer_size = 0;

  /* Calculate the sound buffer size */
  #ifdef MODE_PAL
  snd.buffer_size = (rate / 50);
  #else
  snd.buffer_size = (rate / 60);
  #endif
  snd.sample_rate = rate;

  /* Allocate sound buffers */
  if(!memory_allocated)
  {
    snd.buffer[0] = (int16 *)memalign(32, SND_SIZE);
    snd.buffer[1] = (int16 *)memalign(32, SND_SIZE);
    snd.fm.buffer[0] = (int16 *)memalign(32, SND_SIZE);
    snd.fm.buffer[1] = (int16 *)memalign(32, SND_SIZE);
    snd.psg.buffer = (int16 *)memalign(32, SND_SIZE);
    snd.dac.buffer = (int16 *)memalign(32, SND_SIZE);
  }

  /* Make sure we could allocate everything */
  if(!snd.buffer[0]    ||
     !snd.buffer[1]    ||
     !snd.fm.buffer[0] ||
     !snd.fm.buffer[1] ||
     !snd.psg.buffer   ||
     !snd.dac.buffer)
    return (0);

  memory_allocated = 1;

  /* Initialize sound chip emulation */
  SN76496_sh_start(zclk, 100, rate);
  YM2612Init(1, vclk, rate, NULL, NULL);

  /* Set audio enable flag */
  snd.enabled = 1;

  /* Make sound table */
  for (i = 0; i < 262; i++)
  {
    float p = snd.buffer_size * i;
    p = p / 262;
    sound_tbl[i] = (int)p;
  }

  return (1);
}

void audio_shutdown(void)
{
}

void system_init(int cpu68k_type, int xvdp)
{
  use_cpu68k_type = cpu68k_type;

  gen_init(use_cpu68k_type);
  vdp_init(use_cpu68k_type, xvdp);
  xrender_init(xvdp);
}

void system_reset(void)
{
  gen_reset();
  vdp_reset();
  render_reset();
  io_rescan();

  if(snd.enabled)
  {
    YM2612ResetChip(0);
    memset(snd.buffer[0], 0, SND_SIZE);
    memset(snd.buffer[1], 0, SND_SIZE);
    memset(snd.fm.buffer[0], 0, SND_SIZE);
    memset(snd.fm.buffer[1], 0, SND_SIZE);
    memset(snd.psg.buffer, 0, SND_SIZE);
    memset(snd.dac.buffer, 0, SND_SIZE);
  }
}

void system_shutdown(void)
{
	sram_save();
	gen_shutdown();
	vdp_shutdown();
	render_shutdown();
}

int system_frame()
{
  int line;

  if(gen_running == 0)
    return 0;

  /* Clear V-Blank flag */
  status &= ~0x0008;

  /* Toggle even/odd flag (IM2 only) */
  if(im2_flag)
    status ^= 0x0010;

  /* Point to start of sound buffer */
  snd.dac.lastStage = snd.dac.curStage = 0;

  /* Parse sprites for line 0 (done on line 261) */
  parse_satb(0x80);

  for(line = 0; line < 262; line += 1)
  {
    /* Used by HV counter */
    v_counter = line;

    /* I/O Timers */
    io_scanline();
        
    /* Run Z80 emulation (if enabled) */
    if(zreset == 1 && zbusreq == 0)
    {
      Cz80_Exec(&CZ80, dc_options.z80_clock);
      if(!gen_running)
        break;
    }

    /* Run 68000 emulation */
		#if defined MODE_PAL
    M68K_Exec(use_cpu68k_type, 585);
		#else
    M68K_Exec(use_cpu68k_type, 487);
		#endif
    
    if(!gen_running)
      break;

    /* If a Z80 interrupt is still pending after a scanline, cancel it */
    if(zirq == 1)
      zirq = 0;

    if(line <  frame_end   )
      render_line(line);
    if(line <  frame_end-1 )
      parse_satb(0x81 + line);

    /* Do H interrupt processing */
    if(line <= frame_end)
    {
      if(--counter == -1)
      {
        counter = reg[10];
        hint_pending = 1;

        if(reg[0] & 0x10)
          M68K_SetIRQ(use_cpu68k_type, 4);
      }
    }
    else
      counter = reg[10];

    /* Do end of frame processing */
    if(line == frame_end)
    {
      status |= 0x0088;
      vint_pending = 1;

      /* ---------------------------------------------------------- */
      /* Give enough time to read the interrupt pending flag before */
      /* the interrupt actually occurs.                             */
      /* ---------------------------------------------------------- */
      M68K_Exec(use_cpu68k_type, 16);

      if(!gen_running)
        break;

      if(reg[1] & 0x20)
        M68K_SetIRQ(use_cpu68k_type, 6);

      if(zreset == 1 && zbusreq == 0)
      {
        Cz80_Set_IRQ(&CZ80, 0);
        zirq = 1;
      }
    }
 
    fm_update_timers();

    snd.dac.curStage = sound_tbl[line];
  }

  if(snd.enabled)
    audio_update();

  return gen_running;
}

int system_frame_x()
{
  int line;

  if(gen_running == 0)
    return 0;

  /* Clear V-Blank flag */
  status &= ~0x0008;

  /* Toggle even/odd flag (IM2 only) */
  if(im2_flag)
    status ^= 0x0010;

  /* Point to start of sound buffer */
  snd.dac.lastStage = snd.dac.curStage = 0;

  /* Set the initial colour palette */
  //	xrender_set_palette();
  //	is_color_dirty = 0;

  for(line = 0; line < 262; line += 1)
  {
    /* Used by HV counter */
    v_counter = line;

    /* I/O Timers */
    io_scanline();
        
    /* Run Z80 emulation (if enabled) */
    if(zreset == 1 && zbusreq == 0)
    {
      Cz80_Exec(&CZ80, dc_options.z80_clock);
      if(!gen_running)
        break;
    }

    /* Run 68000 emulation */
		#if defined MODE_PAL
		M68K_Exec(use_cpu68k_type, 585);
		#else
    M68K_Exec(use_cpu68k_type, 487);
		#endif
    if(!gen_running)
      break;

    /* If a Z80 interrupt is still pending after a scanline, cancel it */
    if(zirq == 1)
      zirq = 0;

    /* Do H interrupt processing */
    if(line <= frame_end)
    {
      if(--counter == -1)
      {
        counter = reg[10];
        hint_pending = 1;

        if(reg[0] & 0x10)
          M68K_SetIRQ(use_cpu68k_type, 4);
      }
    }
    else
      counter = reg[10];

    /* Do end of frame processing */
    if(line == frame_end)
    {
      status |= 0x0088;
      vint_pending = 1;

      /* ---------------------------------------------------------- */
      /* Give enough time to read the interrupt pending flag before */
      /* the interrupt actually occurs.                             */
      /* ---------------------------------------------------------- */
      M68K_Exec(use_cpu68k_type, 16);

      if(!gen_running)
        break;

      if(reg[1] & 0x20)
        M68K_SetIRQ(use_cpu68k_type, 6);

      if(zreset == 1 && zbusreq == 0)
      {
        Cz80_Set_IRQ(&CZ80, 0);
        zirq = 1;
      }
    }
 
    fm_update_timers();

    snd.dac.curStage = sound_tbl[line];
  }

  if(snd.enabled)
    audio_update();

  return gen_running;
}

#define PSG_VOLUME 3
#define FM_VOLUME 2

void audio_update(void)
{
  int i;
  int16 tmp,acc;
  int16 *tempBuffer[2];

  tempBuffer[0] = snd.fm.buffer[0];
  tempBuffer[1] = snd.fm.buffer[1];

  // Do whole frame of FM and PSG
  YM2612UpdateSynth(0, (int16 **)tempBuffer, snd.buffer_size);
  SN76496Update(0, snd.psg.buffer, snd.buffer_size);

  // Finish the DAC to the end of the frame
  YM2612UpdateDacAndTimers(0, snd.dac.buffer + snd.dac.lastStage, snd.buffer_size - snd.dac.lastStage);

  for(i=0; i<snd.buffer_size; i++)
  {
    tmp = (snd.psg.buffer[i] / PSG_VOLUME) + snd.dac.buffer[i];

    acc = tmp + (snd.fm.buffer[0][i] / FM_VOLUME);
    snd.buffer[0][i] = acc;

    tmp += snd.fm.buffer[1][i] / FM_VOLUME;
    snd.buffer[1][i] = tmp;
  }
}
